"use strict";
const Service = require("egg").Service;
const { httpCode } = require("../../utils/userConfig")
const crypto = require('crypto');
const AuthException = require('../../exception/authException');
const SystemErrException = require("../../exception/systemError");
const Qiniu = require("../../utils/qiniu");
const fs = require('fs');
const path = require('path');
const awaitWriteStream = require("await-stream-ready").write;
const md5 = require('md5');
const await = require("await-stream-ready/lib/await");
class UserService extends Service {

  async login(username, password) {
    try {
      const { ctx, app } = this;
      const user = await ctx.model.Sys.User.findOne({
        where: {
          user_name: username
        },
        raw: true
      });
      if (!user) {
        return {
          state: false,
          code: httpCode.LOGICERROR,
          message: "用户名或密码错误！"
        }
      }
      let x = crypto.createHash('md5').update(password).digest('hex');
      if (crypto.createHash('md5').update(password).digest('hex') === user.password) {
        const { id, userName, appId } = user;
        const token = app.jwt.sign({
          userId: id,
          appId,
          userName
        }, app.config.jwt.secret);
        ctx.cookies.set('token', token, {
          maxAge: 1000 * 3600 * 24,
          httpOnly: false,
          signed: false
        });
        return {
          data: token,
          code: httpCode.SUCCESS,
          message: '登录成功！'
        }
      } else {
        return {
          code: httpCode.LOGICERROR,
          message: '用户名或密码错误！'
        }
      }
    } catch (error) {
      throw new SystemErrException("登录异常！" + error);
    }
  }

  async updateAuthority(postData) {
    let transaction;
    try {
      const { ctx } = this;
      transaction = await this.ctx.model.transaction();
      // 创建关联关系
      const userRoles = postData.selectAuthorityIds.map(item => {
        return {
          userId: postData.userId,
          roleId: item
        }
      })
      // 删除之前的用户权限
      await ctx.model.Sys.UserRole.destroy({
        where: { user_id: postData.userId }
      })
      await ctx.model.Sys.UserRole.bulkCreate(userRoles);
      return {
        code: httpCode.SUCCESS,
        message: "权限编辑成功"
      }
    } catch (error) {
      this.ctx.logger.error(error);
      await transaction.rollback();
    }
  }

  async add(postData) {
    let transaction;
    try {
      const { ctx } = this;
      transaction = await this.ctx.model.transaction();
      // 如果是试题反馈的编辑，可能会有其他相同的试题需要修改
      let parts = ctx.multipart({ autoFields: true });
      let stream;
      let uploadFiles = {};
      while ((stream = await parts()) != null) {
        if (!stream.filename) {
          break;
        }
        let fieldname = stream.fieldname;
        if (['privateKeyFile', 'publicKeyFile'].indexOf(fieldname) != -1) {
          let fileName = `${md5(new Date().getTime())}_${stream.filename}`
          const localFilePath = path.join(__dirname, "../../public/wxPayFile", fileName);
          const writeStream = fs.createWriteStream(localFilePath);
          await awaitWriteStream(stream.pipe(writeStream));
          uploadFiles[fieldname] = fileName
        } else {
          uploadFiles['avatar'] = await Qiniu.uploadImg(stream); // 新增图片
        }
      }
      let user = { ...parts.field, ...uploadFiles }
      user.password = crypto.createHash('md5').update(user.password).digest('hex');
      delete user.id;
      await ctx.model.Sys.User.create(user);
      return {
        code: httpCode.SUCCESS,
        message: "用户添加成功",
      };
    } catch (error) {
      await transaction.rollback();
      throw new SystemErrException("用户添加异常：" + error);
    }
  }

  async getInfo(id) {
    try {
      const { ctx, app } = this;
      let { currentUserName } = ctx;

      const user = await ctx.model.Sys.User.findByPk(id);
      // 获取所有权限
      let currentUser = await ctx.model.Sys.User.findAll({
        where: { id },
        include: {
          model: app.model.Sys.Role,
          include: {
            model: app.model.Sys.Menu,
            order: [["order", "asc"]]
          }
        }
      });
      const userAuthority = JSON.parse(JSON.stringify(currentUser));
      const userData = userAuthority[0];
      let userRoles = userData.SRoles.map(role => {
        return role.SMenus;
      }).flat(Infinity);

      // 权限去重
      let filterRoles = [];
      let roleIdsObj = {};
      userRoles.forEach(item => {
        if (!roleIdsObj[item.id]) {
          filterRoles.push(item);
          roleIdsObj[item.id] = true;
        }
      })
      let roles = filterRoles.map(item => {
        if (item.type === 2) {
          return item.permission
        }
      }).filter(Boolean);
      // 组装菜单树
      let menus = (function makeTreeData(arr, parentId) {
        let temp = []
        for (let i = 0; i < arr.length; i++) {
          if (arr[i].parentId === parentId) {
            let item = arr[i];
            let menu = {
              id: item.id,
              name: item.title,
              component: item.permission,
              type: item.type,
              order: item.order
            }
            if (menu.type === 1) {
              menu.icon = item.icon
              menu.path = item.url;
              menu.order = item.order
              menu.meta = { title: item.title, icon: 'dashboard', noCache: true }
            }
            temp.push(menu);
            let children = makeTreeData(filterRoles, menu.id);
            if (children.length > 0) {
              if (children[0].type == 1) {
                menu.children = makeTreeData(filterRoles, menu.id);
                menu.redirect = `${children[0].path}`;
                menu.component = 'Layout';
              } else {
                let pageBtns = makeTreeData(filterRoles, menu.id);
                menu.btns = pageBtns;
              }
            }
          }
        }
        return temp
      })(filterRoles, null)

      // 排序
      menus.forEach((item, index) => {
        if (!item.children) {
          let temp = JSON.parse(JSON.stringify(item));
          let result = {
            path: temp.path,
            component: 'Layout',
            children: [
              {
                path: 'index',
                name: temp.name,
                component: temp.component,
                meta: temp.meta
              }
            ]
          }
          menus[index] = result;
        }
      });

      // 对用户菜单就行处理
      menus = menus.map(menu => {
        if (menu.name?.indexOf('(customer)') != -1) {
          menu = menu.children;
        }
        return menu;
      }).flat();

      menus = menus.sort((a, b) => a.order - b.order);

      menus.forEach(menu => {
        menu.children = menu.children.sort((a, b) => a.order - b.order)
      })

      const result = {
        name: userData.userName,
        avatar: `${app.config.userConfig.imgDomain}/${userData.avatar}`,
        roles,
        menus
      }

      if (menus.length === 0) {
        return {
          data: result,
          code: httpCode.LOGICERROR,
          message: '当前帐号暂无任何权限，请联系管理员!'
        }
      }
      // 将权限加入token
      const newToken = app.jwt.sign({
        roles: JSON.stringify(roles)
      }, app.config.jwt.secret);
      ctx.cookies.set('roles', newToken, {
        maxAge: 1000 * 3600 * 24,
        httpOnly: false,
        signed: false
      });
      return {
        data: result,
        code: httpCode.SUCCESS,
        message: '成功获取用户'
      }
    } catch (error) {
      throw new SystemErrException("获取用户信息异常：" + error);
    }
  }

  async deleteUser(id) {
    try {
      const { ctx } = this;
      const user = await ctx.model.Sys.User.findOne({
        where: {
          id
        },
        raw: true
      });
      if (user.userName === 'superAdmin') {
        return {
          code: httpCode.LOGICERROR,
          message: '超级管理员帐号禁止删除！'
        }
      }
      if (user.avatar) {
        await ctx.service.common.file.deleteFile({
          name: user.avatar
        });
      }
      // 删除用户信息
      await ctx.model.Sys.User.destroy({
        where: { id }
      })

      // 删除之前的用户权限
      await ctx.model.Sys.UserRole.destroy({
        where: { userId: id }
      })
      return {
        code: httpCode.SUCCESS,
        message: '删除成功！'
      }
    } catch (error) {
      this.ctx.logger.info(error);
    }
  }

  async getUsers() {
    try {
      const { ctx, app } = this;
      let users = await ctx.model.Sys.User.findAll({
        order: [['create_date', 'desc']], // 排序规则 
        include: {
          model: app.model.Sys.Role
        }
      });
      users = JSON.parse(JSON.stringify(users))
      users.forEach(item => {
        item.roles = item.SRoles.map(role => {
          return {
            id: role.id,
            name: role.name
          }
        });
        item.avatar = `${app.config.userConfig.mediasBaseUrl}/${item.avatar}`;
        item.password = "";
        delete item.Roles;
        for (const key in item) {
          let value = item[key];
          if (value == 'null') {
            item[key] = ''
          }
        }
      })
      return {
        data: users,
        code: httpCode.SUCCESS,
        message: '用户查询成功！'
      }
    } catch (error) {
      throw new SystemErrException("用户查询异常：" + error);
    }
  }

  async updateUser(user) {
    let transaction;
    try {
      const { ctx } = this;
      transaction = await this.ctx.model.transaction();
      let { id, avatar } = user;
      user.avatar = user.avatar.split('medias/').pop();
      // 如果没有输入新密码，则不做修改
      if (!user.password || user.password == 'null') {
        delete user.password;
      } else {
        user.password = crypto.createHash('md5').update(user.password).digest('hex');
      }
      // 新旧文件对比 
      const userOld = await ctx.model.Sys.User.findOne({
        where: {
          id
        },
        raw: true
      });
      if (user.avatar != userOld.avatar && userOld.avatar) {
        await ctx.service.sys.file.deleteFile({
          name: userOld.avatar
        });
      }
      await ctx.model.Sys.User.update(user, {
        where: { id }
      })
      return {
        code: httpCode.SUCCESS,
        message: '用户更新成功！'
      }
    } catch (error) {
      await transaction.rollback();
      throw new SystemErrException("用户更新异常：" + error);
    }
  }

  async getUserAuthority(id) {
    try {
      const { ctx, app } = this;
      const allAuthority = await ctx.model.Sys.User.findAll({
        where: { id },
        include: {
          model: app.model.Sys.Role
        }
      })
      const result = JSON.parse(JSON.stringify(allAuthority))[0];
      let rolesId = [];
      if (result.Roles && result.Roles.length > 0) {
        rolesId = result.Roles.map(item => item.id)
      }
      return {
        data: {
          rolesId
        },
        code: httpCode.SUCCESS,
        message: '权限查询成功！'
      }
    } catch (error) {
      throw new SystemErrException("权限查询异常：" + error);
    }
  }

  // 获取小程序端的信息，对应的管理员
  async verifyApp(wxAppId) {
    try {
      let currentUser = await this.ctx.model.Sys.User.findAll({
        where: { wxAppId },
        attributes: ['id', 'userName', 'wxAppId', 'wxAppSecret'],
        raw: true
      });
      return currentUser[0];
    } catch (error) {
      this.ctx.logger.error(error);
      throw new SystemErrException("获取小程序端的信息，对应的管理员异常：" + error);
    }
  }

  // 验证web端的token
  async verifyToken(token, rolesToken) {
    try {
      if (!token) { // 如果token不存在就抛出异常
        throw new AuthException("用户凭证丢失，需重新登录！");
      }
      const { app } = this;
      const secret = app.config.jwt.secret;
      try {
        await app.jwt.verify(token, secret);
        const rolesData = await this.app.jwt.decode(rolesToken);
        const userInfo = await this.app.jwt.decode(token);
        if (!userInfo) {
          throw new AuthException("无用户相关信息！");
        }
        const { userId, userName, appId } = userInfo;
        return {
          userName,
          userId,
          appId,
          roles: rolesData && JSON.parse(rolesData.roles)
        };
      } catch (e) {
        // 通过消息判断token是否过期
        if (e.message === 'jwt expired') {
          throw new AuthException('令牌过期', httpCode.LOGINEXPIREERROR);
        }
        throw new AuthException("令牌过期了...");
      }
    } catch (error) {
      this.ctx.logger.info(error);
    }
  }
}

module.exports = UserService;
